import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;

/**
 * ClassName: MazePanel
 * Package: PACKAGE_NAME
 *
 * @Author: shangyuan_wu
 * @Create: 2022/12/15 - 14:20
 * @see #createBlocks()  用于创建Block的网格
 * @see #drawBlock(Graphics) 用于在画布上画出Block的网格
 * @see #paint(Graphics) 重写paint方法，完成对画布的更新
 * @see #computed() 自动生成迷宫
 * @see #gameWin() 游戏胜利，并弹窗提示
 * @see #gameOver() 游戏失败，并弹窗提示
 * @see #removeWall(Block, Block) 消除掉两个Block之间的 "墙"
 * Description:
 */
public class MazePanel extends JPanel implements Runnable {
    private final boolean START = true;
    private final boolean END = true;
    private boolean gameFlag = START;
    private int i = Constant.MROWS;
    private int j = Constant.MCOLS;
    Block[][] blocks = null;

    public MazePanel() {
        this.setLayout(null);
        this.setOpaque(false);
        createBlocks();
        //绘制米老鼠
        createMouse();
        createRice();
        computed();
        this.setVisible(true);
    }

    private void createBlocks() {
        blocks = new Block[Constant.ROWS][Constant.COLS];
        Block block = null;
        for (int i = 0; i < Constant.ROWS; i++) {
            for (int j = 0; j < Constant.COLS; j++) {
                block = new Block(i, j, Constant.H);
                blocks[i][j] = block;
            }
        }
    }

    private void drawBlock(Graphics g) {
        Block block = null;
        for (int i = 0; i < Constant.ROWS; i++) {
            for (int j = 0; j < Constant.COLS; j++) {
                block = blocks[i][j];
                if (block != null) {
                    block.draw(g);
                }
            }
        }
    }

    private void computed() {
		/*
		1.将起点作为当前迷宫单元并标记为已访问
		2.当还存在未标记的迷宫单元，进行循环
			1).如果当前迷宫单元有未被访问过的的相邻的迷宫单元
				(1).随机选择一个未访问的相邻迷宫单元
				(2).将当前迷宫单元入栈
				(3).移除当前迷宫单元与相邻迷宫单元的墙
				(4).标记相邻迷宫单元并用它作为当前迷宫单元
			2).如果当前迷宫单元不存在未访问的相邻迷宫单元，并且栈不空
				(1).栈顶的迷宫单元出栈
				(2).令其成为当前迷宫单元
		 */
        Random random = new Random();
        Stack<Block> stack = new Stack<>();
        //取中间位置为当前单元
        Block current = blocks[Constant.MROWS][Constant.MCOLS];
        //标记为已访问
        current.setVisited(true);
        //因为第一个已经设置为访问了，所以要减去1
        int unVisitedCount = Constant.ROWS * Constant.COLS - 1;
        //定义邻居
        List<Block> neighbors = null;
        Block next = null;
        while (unVisitedCount > 0) {
            //查找邻居集合(未被访问的)
            neighbors = current.findNeighbors(blocks);
            //如果当前迷宫单元有未被访问过的的相邻的迷宫单元
            if (neighbors.size() > 0) {
                //随机选择一个未访问的相邻迷宫单元
                next = neighbors.get(random.nextInt(neighbors.size()));
                //将当前迷宫单元入栈
                stack.push(current);
                //移除当前迷宫单元与相邻迷宫单元的墙
                removeWall(current, next);
                //标记相邻迷宫单元并用它作为当前迷宫单元
                next.setVisited(true);
                //标记一个为访问，则计数器递减1
                unVisitedCount--;//递减
                current = next;
            } else if (!stack.isEmpty()) {
                /*
                 如果当前迷宫单元不存在未访问的相邻迷宫单元，并且栈不空
                    1.栈顶的迷宫单元出栈
					2.令其成为当前迷宫单元
                 */
                current = stack.pop();
            }
        }
    }


    private void removeWall(Block current, Block next) {
        //横向邻居
        if (current.getI() == next.getI()) {
            if (current.getJ() > next.getJ()) {
                //左边邻居的话，要移除自己的左墙和邻居的右墙
                current.walls[Constant.LEFT] = false;
                next.walls[Constant.RIGHT] = false;
            } else {
                //匹配到的是右边邻居，要移除自己的右墙和邻居的左墙
                current.walls[Constant.RIGHT] = false;
                next.walls[Constant.LEFT] = false;
            }
        } else if (current.getJ() == next.getJ()) {
            //纵向邻居
            if (current.getI() > next.getI()) {
                //匹配到的是上边邻居，要移除自己的上墙和邻居的下墙
                current.walls[Constant.TOP] = false;
                next.walls[Constant.BOTTOM] = false;
            } else {
                //匹配到的是下边邻居，要移除自己的下墙和邻居的上墙
                current.walls[Constant.BOTTOM] = false;
                next.walls[Constant.TOP] = false;
            }
        }
    }

    public void restart() {
		/*参数重置
		1.游戏状态
		2.迷宫单元重置
		3.重新计算线路
		*/

        this.removeAll();
        //1.游戏状态
        gameFlag = START;
        //2.迷宫单元重置
        Block block;
        for (int i = 0; i < Constant.ROWS; i++) {
            for (int j = 0; j < Constant.COLS; j++) {
                block = blocks[i][j];
                if (block != null) {
                    block.setVisited(false);
                    block.walls[Constant.TOP] = true;
                    block.walls[Constant.RIGHT] = true;
                    block.walls[Constant.BOTTOM] = true;
                    block.walls[Constant.LEFT] = true;
                }
            }
        }
        //3.计算处理线路
        computed();
        //米老鼠归零
        createMouse();
        createRice();
        //重绘
        repaint();
    }

    public void gameWin() {
        gameFlag = END;
        //弹出结束提示
        JOptionPane.showMessageDialog(this, "你赢了!");
    }

    public void gameOver() {
        gameFlag = END;
        //弹出结束提示
        JOptionPane.showMessageDialog(this, "你失败了,请再接再厉!");
        restart();
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        //绘制网格
        drawBlock(g);

    }

    public void createMouse() {
        JLabel jLabelMouse = new JLabel(new ImageIcon("Mouse.jpg"));

        jLabelMouse.setBounds(
                Constant.START + Constant.MROWS * Constant.H
                , Constant.START + Constant.MCOLS * Constant.H
                , Constant.H, Constant.H);
        this.add(jLabelMouse);
        repaint();
    }

    public void createRice() {
        JLabel jLabelRice = new JLabel(new ImageIcon("Rice.jpeg"));

        jLabelRice.setBounds(
                Constant.START + (Constant.ROWS - 1) * Constant.H
                , Constant.START + (Constant.COLS - 1) * Constant.H
                , Constant.H, Constant.H);
        this.add(jLabelRice);
        repaint();
    }

    public void mouseMove(int type){
        //根据当前米老鼠所在的位置，判断想要前进的方向有没有 "墙"
        if (!blocks[this.i][this.j].walls[type]) {
            if (type == Constant.TOP) {
                System.out.println("上面没墙");
                i--;
            }
            if (type == Constant.BOTTOM) {
                System.out.println("下面没墙");
                i++;
            }
            if (type == Constant.LEFT) {
                System.out.println("左面没墙");
                j--;
            }
            if (type == Constant.RIGHT) {
                System.out.println("右面没墙");
                j++;
            }
            this.removeAll();
            JLabel jLabelMouse = new JLabel(new ImageIcon("Mouse.jpg"));
            jLabelMouse.setBounds(Constant.START + j * Constant.H
                    , Constant.START + i * Constant.H
                    , Constant.H, Constant.H);
            this.add(jLabelMouse);
            this.createRice();
            repaint();
            //判断如果i,j等于终点的，则表示获得成功
            if (this.i + 1 == Constant.ROWS && this.j + 1 == Constant.COLS) {
                this.gameWin();
            }
        }
    }

    @Override
    public void run() {
        findExitBfs(blocks);
    }

    public  void findExitBfs(Block[][] blocks) {
        //创建一个队列去储存为被访问的结点的位置
        Queue<Block> queue = new LinkedList<>();
        //将开始位置添加到队列
        queue.add(blocks[Constant.MROWS][Constant.MCOLS]);
        //创建一个二维数组去储存已经访问过的结点
        boolean[][] visited = new boolean[Constant.ROWS][Constant.COLS];

        //将开始的结点设置为已经访问
        visited[Constant.MROWS][Constant.MCOLS] = true;

        //定义四个方向
        int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

        //循环，直到队列为空
        while (!queue.isEmpty()) {
            //获取当前位置
            Block current = queue.poll();

            //将当前位置设置成黄色
            current.setColor(Color.YELLOW);

            //更新UI去反应变化
            repaint();

            //在继续之前等在议会
//            try {
//                Thread.sleep(100);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }

            //检查当前位置是不是终点
            if (current.getI() == Constant.ROWS - 1 && current.getJ() == Constant.COLS - 1) {
                JOptionPane.showMessageDialog(this, "找到出口了！");
                break;
            }
            //通过这四个方向继续循环
            for (int[] direction : directions) {
                //计算最新位置
                int newX = current.getI() + direction[0];
                int newY = current.getJ() + direction[1];

                //检查是否这个新的位置有没有超过迷宫的边界
                if (newX >= 0 && newX < Constant.ROWS && newY >= 0 && newY < Constant.COLS) {
                    //检查新位置是否已经被访问
                    if (!visited[newX][newY]) {
                        //将新位置设置为已经访问
                        visited[newX][newY] = true;

                        //将新位置添加到队列
                        queue.add(blocks[newX][newY]);
                    }
                }
            }
        }
    }
}
